/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.camera.one.v2.photo;

import com.android.camera.app.OrientationManager;
import com.android.camera.async.BufferQueue;
import com.android.camera.async.MainThread;
import com.android.camera.one.OneCamera;
import com.android.camera.one.v2.IReprocessExcutor;
import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.one.v2.commands.CameraCommand;
import com.android.camera.one.v2.commands.CameraCommandExecutor;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.RequestBuilder;
import com.android.camera.one.v2.core.ResourceAcquisitionFailedException;
import com.android.camera.one.v2.imagesaver.ImageSaver;
import com.android.camera.one.v2.sharedimagereader.ManagedImageReader;
import com.android.camera.one.v2.sharedimagereader.metadatasynchronizer.MetadataPool;
import com.android.camera.session.CaptureSession;
import com.google.common.base.Objects;

import android.hardware.camera2.CameraAccessException;

class PictureTakerImpl implements PictureTaker {
    private final MainThread mMainExecutor;
    private final CameraCommandExecutor mCameraCommandExecutor;
    private final ImageSaver.Builder mImageSaverBuilder;
    private final ImageCaptureCommand mDefaultCaptureCommand;
    private final BufferQueue<ImageProxy> mZslRingBuffer;
    private final MetadataPool mZslMetadataPool;
    private final FrameServer mFrameServer;
    private final RequestBuilder.Factory mBuilderFactory;
    private final ManagedImageReader mImageReader;
    private final IReprocessExcutor mReprocessExcutor;

    public PictureTakerImpl(MainThread mainExecutor, CameraCommandExecutor cameraCommandExecutor,
            ImageSaver.Builder imageSaverBuilder, ImageCaptureCommand command,
            BufferQueue<ImageProxy> zslRingBuffer, MetadataPool zslMetadataPool,
            FrameServer frameServer, RequestBuilder.Factory rootBuilderFactory, 
            ManagedImageReader imageReader, IReprocessExcutor reprocessExcutor) {
        mMainExecutor = mainExecutor;
        mCameraCommandExecutor = cameraCommandExecutor;
        mImageSaverBuilder = imageSaverBuilder;
        mDefaultCaptureCommand = command;
        mZslRingBuffer = zslRingBuffer;
        mZslMetadataPool = zslMetadataPool;
        mFrameServer = frameServer;
        mBuilderFactory = rootBuilderFactory;
        mImageReader = imageReader;
        mReprocessExcutor = reprocessExcutor;
    }

    private final class PictureTakerCommand implements CameraCommand {
        private final PictureCallbackAdapter mPictureCallbackAdapter;
        private final ImageSaver mImageSaver;
        private final CaptureSession mSession;
        private final ImageCaptureCommand mCommand;

        private PictureTakerCommand(PictureCallbackAdapter pictureCallbackAdapter,
                ImageSaver imageSaver,
                CaptureSession session,
                ImageCaptureCommand command) {
        	mPictureCallbackAdapter = pictureCallbackAdapter;
            mImageSaver = imageSaver;
            mSession = session;
            mCommand = command;
        }

        @Override
        public void run() throws InterruptedException, CameraAccessException,
                CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
            try {
                mCommand.run(mPictureCallbackAdapter, mImageSaver);
            } catch (Exception e) {
                mSession.cancel();
                throw e;
            }
        }

        @Override
        public String toString() {
            return Objects.toStringHelper(this)
                    .add("command", mCommand)
                    .toString();
        }
    }

    @Override
    public void takePicture(OneCamera.PhotoCaptureParameters params, final CaptureSession session,
    		ImageCaptureCommandExt thirdCaptureCommand) {
        OneCamera.PictureCallback pictureCallback = params.callback;

        // Wrap the pictureCallback with a thread-safe adapter which guarantees
        // that they are always invoked on the main thread.
        PictureCallbackAdapter pictureCallbackAdapter =
                new PictureCallbackAdapter(pictureCallback, mMainExecutor);

        final ImageSaver imageSaver = mImageSaverBuilder.build(
                params.saverCallback,
                OrientationManager.DeviceOrientation.from(params.orientation),
                session);

        if (thirdCaptureCommand == null) {
	        mCameraCommandExecutor.execute(new PictureTakerCommand(
	        		pictureCallbackAdapter, imageSaver, session, mDefaultCaptureCommand));
        } else {
        	thirdCaptureCommand.initCaptureEnv(mZslRingBuffer, mZslMetadataPool, 
        			mFrameServer, mBuilderFactory, mImageReader, mReprocessExcutor);
        	mCameraCommandExecutor.execute(new PictureTakerCommand(
        			pictureCallbackAdapter, imageSaver, session, thirdCaptureCommand));
        }
    }
}
